package com.yang.aspect;



import com.alibaba.fastjson2.JSON;
import com.yang.domain.entity.ExceptionLog;
import com.yang.asynLog.AsyncFactory;
import com.yang.asynLog.AsyncManager;
import com.yang.mapper.ExceptionLogMapper;
import com.yang.utils.IpUtils;
import com.yang.utils.UserAgentUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.*;

/**
 * <p>
 * AOP记录异常日志
 * </p>
 * @author yang
 * @date 2023/6/25
 */
@Aspect
@Component
@Slf4j
public class ExceptionLogAspect {

    /**
     * 设置操作异常日志切入点，扫描所有controller包下的操作
     */
    @Pointcut("execution(* com.yang.controller..*.*(..))")
    public void exceptionLogPointCut() {
    }
    @Autowired
    private ExceptionLogMapper exceptionLogMapper;




    /**
     * 异常通知，只有连接点异常后才会执行
     *
     * @param joinPoint 切面方法的信息
     * @param e         异常
     */
    @AfterThrowing(pointcut = "exceptionLogPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        // 获取实体类参数
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
        Api api = (Api) signature.getDeclaringType().getAnnotation(Api.class);
        ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
        Map<String, String> userAgentMap = UserAgentUtils.parseOsAndBrowser(request.getHeader("User-Agent"));
        String ip = IpUtils.getIpAddress(request);
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = method.getName();
        methodName = className + "." + methodName;

        // 封装实体类
        ExceptionLog exceptionLog = ExceptionLog
                .builder()
                .module(api.tags()[0])
                .uri(request.getRequestURI())
                .name(e.getClass().getName())
                .description(apiOperation.value())
                .errorMethod(methodName)
                .message(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace()))
                .params(JSON.toJSONString(joinPoint.getArgs()))
                .requestMethod(Objects.requireNonNull(request).getMethod())
                .ipAddress(ip)
                .ipSource(IpUtils.getIpSource(ip))
                .os(userAgentMap.get("os"))
                .browser(userAgentMap.get("browser"))
                .createTime(LocalDateTime.now()).build();

        // 异步保存到数据库
        // AsyncManager.getInstance().execute(AsyncFactory.recordException(exceptionLog));

        // 保存异常日志
        exceptionLogMapper.insert(exceptionLog);


    }

    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
        StringBuilder stringBuilder = new StringBuilder();
        for (StackTraceElement stet : elements) {
            stringBuilder.append(stet).append("\n");
        }
        return exceptionName + ":" + exceptionMessage + "\n" + stringBuilder;
    }

}
